/*
Pyxis 2.0
Copyright 2010-2011 Thomas W. Holtquist

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

using System;
using System.Collections;

using Microsoft.SPOT;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Touch;

using Skewworks.Pyxis.EXT;
using Skewworks.Pyxis.Kernel;

namespace Skewworks.Pyxis.GUI.Controls
{
    [Serializable]
    public class RichTextLabel : Control
    {

        #region Structures

        [Serializable]
        private struct RichText
        {
            public string FontName;
            public bool Italic;
            public bool Bold;
            public bool Underline;
            public string Text;
            public RichText(string Text, string FontName, bool Bold, bool Italic, bool Underline)
            {
                this.Text = Text;
                this.FontName = FontName;
                this.Bold = Bold;
                this.Italic = Italic;
                this.Underline = Underline;
            }
        }

        #endregion

        #region Variables

        private string _text = "";
        private bool _autosize = true;
        private string _fontName = "Arial";
        private Color _color = Color.Black;
        private ArrayList _parts = null;

        #endregion

        #region Constructors

        public RichTextLabel(string text, int x, int y)
        {
            _text = text;
            _x = x;
            _y = y;
            ParseText();
        }

        public RichTextLabel(string text, int x, int y, int width, int height)
        {
            _text = text;
            _x = x;
            _y = y;
            _w = width;
            _h = height;
            _autosize = false;
            ParseText();
        }

        public RichTextLabel(string text, Color ForeColor, int x, int y, int width, int height)
        {
            _text = text;
            _x = x;
            _y = y;
            _w = width;
            _h = height;
            _color = ForeColor;
            _autosize = false;
            ParseText();
        }

        public RichTextLabel(string text, Color ForeColor, string FontName, int x, int y, int width, int height)
        {
            _text = text;
            _x = x;
            _y = y;
            _w = width;
            _h = height;
            _color = ForeColor;
            _fontName = FontName;
            _autosize = false;
            ParseText();
        }

        public RichTextLabel(string text, Color ForeColor, string FontName, int x, int y, int width, int height, bool enabled, bool visible)
        {
            _text = text;
            _x = x;
            _y = y;
            _w = width;
            _h = height;
            _color = ForeColor;
            _fontName = FontName;
            _autosize = false;
            _visible = visible;
            _enabled = enabled;
            ParseText();
        }

        #endregion

        #region  Properties

        public string Text
        {
            get { return _text; }
            set
            {
                _text = value;
                ParseText();

                if (_autosize)
                {
                    if (_parent != null) _parent.Render();
                }
                else
                {
                    Render(true);
                }
            }
        }

        public bool AutoSize
        {
            get { return _autosize; }
            set
            {
                _autosize = value;
                if (_parent != null) _parent.Render();
            }
        }

        public string FontName
        {
            get { return _fontName; }
            set
            {
                _fontName = value;
                if (_autosize)
                {
                    if (_parent != null) _parent.Render();
                }
                else
                {
                    Render(true);
                }
            }
        }

        public Color Color
        {
            get { return _color; }
            set
            {
                _color = value;
                Render(true);
            }
        }

        #endregion

        #region GUI

        public override void Render(bool flush)
        {
            int lw = 0;
            int ly = 0;
            int w = _w;
            int h = _h;
            int x = Left;
            int y = Top;
            Font fnt;
            RichText rt;
            int i;

            if (_parent == null || _parent.ScreenBuffer == null || !_visible || _suspend) return;

            // Set clipping region in case we're off parent somewhere (can happen w/ scroll)
            if (_autosize)
            {
                _parent.ScreenBuffer.SetClippingRectangle(_parent.Left, _parent.Top, _parent.Width, _parent.Height);
            }
            else
            {
                rect Region = rect.intersect(new rect(_parent.Left, _parent.Top, _parent.Width, _parent.Height), new rect(Left, Top, _w, _h));
                _parent.ScreenBuffer.SetClippingRectangle(Region.X, Region.Y, Region.Width, Region.Height);
            }

            if (_autosize)
            {
                for (i = 0; i < _parts.Count; i++)
                {
                    rt = (RichText)_parts[i];
                    fnt = FontByName(rt.FontName, rt.Bold, rt.Italic);

                    lw = FontManager.ComputeExtentEx(fnt, rt.Text).Width;
                    _parent.ScreenBuffer.DrawText(rt.Text, fnt, _color, x, y);
                    if (rt.Underline)
                    {
                        ly = fnt.Height + y - fnt.Descent + 1;
                        _parent.ScreenBuffer.DrawLine(_color, 1, x, ly, x + lw, ly);
                    }
                    x += lw;
                    
                    if (x > Left + Width) break;
                }
            }
            else
            {
                for (i = 0; i < _parts.Count; i++)
                {
                    rt = (RichText)_parts[i];
                    fnt = FontByName(rt.FontName, rt.Bold, rt.Italic);

                    // Break text into pieces
                    string[] bits = rt.Text.Split(' ');
                    int e;
                    for (int j = 0; j < bits.Length; j++)
                    {
                        if (j < bits.Length - 1)
                            bits[j] += " ";

                        lw = FontManager.ComputeExtentEx(fnt, bits[j]).Width;
                        e = x + lw;
                        if (e > Left + Width)
                        {
                            x = Left;
                            y += fnt.Height;
                            if (y > Top + Height) break;
                        }

                        if (rt.Underline)
                        {
                            ly = fnt.Height + y - fnt.Descent + 1;
                            _parent.ScreenBuffer.DrawLine(_color, 1, x, ly, x + lw, ly);
                        }

                        _parent.ScreenBuffer.DrawText(bits[j], fnt, _color, x, y);
                        x += FontManager.ComputeExtentEx(fnt, bits[j]).Width;

                    }
                }
            }

            // Flush if needed
            if (flush)
            {
                _parent.ScreenBuffer.Flush(Left, Top, _w, _h);
                _parent.ScreenBuffer.SetClippingRectangle(0, 0, AppearanceManager.ScreenWidth, AppearanceManager.ScreenHeight);
            }
        }

        #endregion

        #region Text Parsing

        private Font FontByName(string name, bool bold, bool italic)
        {
            switch (name.ToLower())
            {
                case "gothic":
                    if (bold && italic) return FontManager.GothicBoldItalic;
                    if (bold) return FontManager.GothicBold;
                    if (italic) return FontManager.GothicItalic;
                    return FontManager.Gothic;
                case "timesroman":
                    if (bold && italic) return FontManager.TimesRomanBoldItalic;
                    if (bold) return FontManager.TimesRomanBold;
                    if (italic) return FontManager.TimesRomanItalic;
                    return FontManager.TimesRoman;
                default:                            // Under all other cases use Arial
                    if (bold && italic) return FontManager.ArialBoldItalic;
                    if (bold) return FontManager.ArialBold;
                    if (italic) return FontManager.ArialItalic;
                    return FontManager.Arial;
            }
        }

        private void ParseText()
        {
            string fnt = _fontName;
            bool bBold = false;
            bool bItalic = false;
            bool bUnderline = false;
            int i;
            int iStart = 0;
            string txt = _text;
            ArrayList fonts = new ArrayList();

            // Reset Array
            _parts = new ArrayList();

            while (true)
            {
                // Get the next index of a Less Than
                i = txt.IndexOf("<", iStart);
                if (i < 0) break;

                // See if we care about it
                if (txt.Substring(i + 1, 1) == "/")
                {
                    // We're looking for a close here
                    if (txt.Substring(i, 4).ToLower() == "</b>")
                    {
                        if (i > 0)
                        {
                            _parts.Add(new RichText(txt.Substring(0, i), fnt, bBold, bItalic, bUnderline));
                        }

                        bBold = false;
                        txt = txt.Substring(i + 4);
                        iStart = 0;
                    }
                    else if (txt.Substring(i, 4).ToLower() == "</i>")
                    {
                        if (i > 0)
                        {
                            _parts.Add(new RichText(txt.Substring(0, i), fnt, bBold, bItalic, bUnderline));
                        }
                        bItalic = false;
                        txt = txt.Substring(i + 4);
                        iStart = 0;
                    }
                    else if (txt.Substring(i, 4).ToLower() == "</u>")
                    {
                        if (i > 0)
                        {
                            _parts.Add(new RichText(txt.Substring(0, i), fnt, bBold, bItalic, bUnderline));
                        }
                        bUnderline = false;
                        txt = txt.Substring(i + 4);
                        iStart = 0;
                    }
                    else if (txt.Substring(i, 7) == "</font>")
                    {
                        if (i > 0)
                        {
                            _parts.Add(new RichText(txt.Substring(0, i), fnt, bBold, bItalic, bUnderline));
                        }

                        fonts.RemoveAt(fonts.Count - 1);
                        if (fonts.Count > 0)
                        {
                            fnt = (string)fonts[fonts.Count - 1];
                        }
                        else
                            fnt = _fontName;

                        txt = txt.Substring(i + 7);
                        iStart = 0;
                    }
                    else
                        iStart = i + 1;
                }
                else
                {

                    // We're looking for a start here
                    if (txt.Substring(i, 3).ToLower() == "<b>")
                    {
                        if (i > 0)
                        {
                            _parts.Add(new RichText(txt.Substring(0, i), fnt, bBold, bItalic, bUnderline));
                        }
                        bBold = true;
                        iStart = 0;
                        txt = txt.Substring(i + 3);
                    }
                    else if (txt.Substring(i, 3).ToLower() == "<i>")
                    {
                        if (i > 0)
                        {
                            _parts.Add(new RichText(txt.Substring(0, i), fnt, bBold, bItalic, bUnderline));
                        }
                        bItalic = true;
                        txt = txt.Substring(i + 3);
                        iStart = 0;
                    }
                    else if (txt.Substring(i, 3).ToLower() == "<u>")
                    {
                        if (i > 0)
                        {
                            _parts.Add(new RichText(txt.Substring(0, i), fnt, bBold, bItalic, bUnderline));
                        }
                        bUnderline = true;
                        txt = txt.Substring(i + 3);
                        iStart = 0;
                    }
                    else if (txt.Substring(i, 6).ToLower() == "<font ")
                    {
                        int end = txt.IndexOf(">", i);   // Find the end of the tag

                        if (end > 0)
                        {
                            // Get the segment and replace double quotes with single quotes
                            string strName = StringReplace(txt.Substring(i + 6, end - i - 6), "\"", "'");

                            int e = strName.IndexOf("'");
                            if (e > 0)
                            {
                                strName = strName.Substring(e + 1);
                                e = strName.IndexOf("'");
                                if (e > 0)
                                {
                                    strName = strName.Substring(0, e);

                                    // Valid Tag; add and trim
                                    if (i > 0)
                                    {
                                        _parts.Add(new RichText(txt.Substring(0, i), fnt, bBold, bItalic, bUnderline));
                                    }
                                    txt = txt.Substring(end + 1);
                                    iStart = 0;

                                    // Add Font
                                    fnt = strName;
                                    fonts.Add(strName);

                                }
                                else
                                    iStart = i + 1; // Invalid tag
                            }
                            else
                                iStart = i + 1; // Invalid tag

                        }
                        else
                            iStart = i + 1; // Invalid tag
                    }
                    else
                        iStart = i + 1;

                }
            }

            if (txt.Length > 0)
                _parts.Add(new RichText(txt, fnt, bBold, bItalic, bUnderline));
        }

        private string StringReplace(string Source, string ToFind, string ReplaceWith)
        {
            int i;
            int iStart = 0;

            if (ToFind == string.Empty)
                return Source;

            while (true)
            {
                i = Source.IndexOf(ToFind, iStart);
                if (i < 0) break;

                if (i > 0)
                {
                    Source = Source.Substring(0, i) + ReplaceWith + Source.Substring(i + ToFind.Length);
                }
                else
                {
                    Source = ReplaceWith + Source.Substring(i + ToFind.Length);
                }

                iStart = i + ReplaceWith.Length;
            }
            return Source;
        }

        #endregion

    }
}
